package gmail;

import gmail.structure.GMConversationEntry;
import gmail.structure.GMSendMailBody;
import gmail.structure.GMThread;

import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Locale;
import java.util.Random;
import java.util.TreeMap;
import java.util.Vector;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.swing.ImageIcon;
import javax.swing.JLabel;
import javax.swing.JOptionPane;

import org.apache.commons.httpclient.Cookie;
import org.apache.commons.httpclient.Header;
import org.apache.commons.httpclient.HttpStatus;
import org.apache.commons.httpclient.NameValuePair;
import org.apache.commons.httpclient.methods.GetMethod;
import org.apache.commons.httpclient.methods.PostMethod;
import org.apache.commons.httpclient.util.URIUtil;

import core.connector.ConnectionDetails;
import core.connector.email.http.HttpConnectorBase;

/**
 * Main class to establish connection to GMail and perform actions
 * Since 0.3.6, underlying HTTP is done by Multi-Threaded HTTPClient
 * Thus multiple request can be sent at same time
 * @author siuying
 * @version 0.3.17
 */
public abstract class GMConnector extends HttpConnectorBase
{
    /* declear constants */
    private final static String LINK_GMAIL = Messages.getString("GM_LINK_GMAIL");
    private final static String LINK_LOGIN = Messages.getString("GM_LINK_LOGIN");
    private final static String LINK_LOGOUT = Messages.getString("GM_LINK_LOGOUT");
    private final static String LINK_REFER = Messages.getString("GM_REFER");
    private final static String LINK_ATTACHMENT = Messages.getString("GM_ATTACHMENT");
    private final static String USER_AGENT = Messages.getString("GM_AGENT");
    private final static String LOGON_SITE = Messages.getString("GM_LOGON_SITE");

    private static Random rdm = new Random();
    private String timezone;


    /**
     * Create an GMConnector object to connect and request gmail for various tasks
     */
    public GMConnector(ConnectionDetails connectionDetails)
    {
        super(connectionDetails);
        timezone = "";
    }
    /**
     * Get timezone of the connection
     * @return String A Integer String
     */
    public String getTimezone() {
        return timezone;
    }

    /**
     * Set timezone of the connection
     * @param timezone String
     */
    public void setTimezone(String timezone) {
        this.timezone = timezone;
    }
    /**
     * disconnect to gmail network
     */
    public synchronized void disconnect()
    {
        System.out.println("DESCONECTANDO...");
        if (!this.isConnected())
        {
            return;
        }

        GetMethod get = new GetMethod(LINK_LOGOUT);
        get.addRequestHeader("referer", LINK_REFER);
        get.addRequestHeader("Content-Type", "text/html");

        client.getState().addCookie(new Cookie(LOGON_SITE, "GMAIL_LOGIN",
                "T" + Calendar.getInstance().getTimeInMillis() + "/" + Calendar.getInstance().getTimeInMillis() + "/" + Calendar.getInstance().getTimeInMillis(),
                "/", 999, true));

        if (!timezone.equals(""))
        {
            client.getState().addCookie(new Cookie(LOGON_SITE, "TZ", timezone, "/", 999, true));
        }

        try
        {
            // execute the method.
            this.status = GMConnector.STATUS_INITED;
            client.executeMethod(get);

        }
        catch (IOException e)
        {

        }
    }
    /**
     * Connect and login to gmail
     * @return boolean
     */
    public synchronized boolean connect()
    {
        System.out.println("CONECTANDO...");
        if (user.equals("") || passwd.equals(""))
        {
            throw new IllegalStateException("Cannot connect: user and password not specified.");
        }

        /* create first post request to login */
        PostMethod post = new PostMethod(LINK_LOGIN);

        // parameters
        NameValuePair[] data = {
                new NameValuePair("service", "mail"),
                new NameValuePair("Email", user),
                new NameValuePair("Passwd", passwd),
                new NameValuePair("null", "Sign in"),
                new NameValuePair("continue", Messages.getString("GM_LINK_GMAIL"))
        };

        post.addRequestHeader("referer", LINK_REFER);
        post.addRequestHeader("Content-Type", "application/x-www-form-urlencoded");
        post.addRequestHeader("Accept-Encoding", "gzip,deflate");
        post.setRequestBody(data);

        int statusCode = -1;
        try
        {
            // execute the method.
            statusCode = client.executeMethod(post);

        }
        catch (IOException e)
        {
            return false;

        }

        // Check we actually get status code correctly
        if (statusCode == -1)
        {
            return false;

        }

        // Get response String & Release the connection.
        String result = null;
        try{
            result = getResponseBodyAsString(post);
        }
        catch(IOException oe)
        {
            return false;
        }
        post.releaseConnection();

        // check if connect failed
        if (result.indexOf("errormsg") > 0)
        {

        	try
        	{
        		int formIni  = result.indexOf("<form");
        		int formEnd = result.indexOf("</form", formIni);
        		String form = result.substring(formIni,formEnd);

                // check if connect was failed because without token
        		if(form.contains("logintoken"))
        		{
        		// connect with login token image
        			return connect_with_logintoken(form);
        		}
        		else
        		{
        			return false;
        		}

        	}
        	catch(Exception ex){return false;}

        }

        // get _sgh variable from login response
        String var_sgh = "";
        try
        {
            int pointer = result.indexOf("_sgh");
            if(pointer > -1)
            {

                int varDeclStart = result.indexOf("_sgh%3D",pointer) + 7;
                int varDeclEnd = result.indexOf("&",varDeclStart);
                var_sgh = result.substring(varDeclStart, varDeclEnd);
            }

        }
        catch (IndexOutOfBoundsException  iooe)
        {
            return false;
        }

        /* create a get request to perform "cookie-handshaking"...  */
        // https://gmail.google.com/gmail?_sgh=
        GetMethod get = new GetMethod(LINK_GMAIL + "?_sgh=" + var_sgh);
        get.addRequestHeader("referer", LINK_REFER);
        get.addRequestHeader("Content-Type", "text/html");
        get.addRequestHeader("Accept-Encoding", "gzip,deflate");

        statusCode = -1;
        try
        {
            // execute the method.
            statusCode = client.executeMethod(get);
        }
        catch (IOException e)
        {
            return false;
        }

        // redirect?
        if (statusCode == HttpStatus.SC_MOVED_PERMANENTLY  ||
                statusCode == HttpStatus.SC_MOVED_TEMPORARILY  ||
                statusCode == HttpStatus.SC_SEE_OTHER ||
                statusCode == HttpStatus.SC_TEMPORARY_REDIRECT )
        {

            String redirectLocation;
            Header locationHeader = get.getResponseHeader("location");
            if (locationHeader != null)
            {
                redirectLocation = locationHeader.getValue();
                get = new GetMethod(redirectLocation);
                get.addRequestHeader("Accept-Encoding", "gzip,deflate");

                try
                {
                    statusCode = client.executeMethod(get);
                }
                catch (IOException e)
                {
                    return false;
                }
            }
            else
            {
                // The response is invalid and did not provide the new location for
                // the resource.
                return false;
            }
        }

        // 2nd redirect?
        if (statusCode == HttpStatus.SC_MOVED_PERMANENTLY  ||
                statusCode == HttpStatus.SC_MOVED_TEMPORARILY  ||
                statusCode == HttpStatus.SC_SEE_OTHER ||
                statusCode == HttpStatus.SC_TEMPORARY_REDIRECT )
        {

            String redirectLocation;
            Header locationHeader = get.getResponseHeader("location");
            if (locationHeader != null)
            {
                redirectLocation = locationHeader.getValue();
                get = new GetMethod(redirectLocation);
                get.addRequestHeader("Accept-Encoding", "gzip,deflate");

                try
                {
                    statusCode = client.executeMethod(get);
                }
                catch (IOException e)
                {
                    return false;
                }
            }
            else
            {
                // The response is invalid and did not provide the new location for
                // the resource.
                return false;
            }
        }

        String resp = null;
        try
        {
            if (statusCode == HttpStatus.SC_OK)
            {
                resp = getResponseBodyAsString(get);
            }
            get.releaseConnection();
        }
        catch(IOException ioe)
        {
        }

        if (resp == null)
        {
            return false;
        }

        this.status = STATUS_CONNECTED;

        return true;
    }



    public synchronized boolean connect_with_logintoken(String form)
    {

        PostMethod post = new PostMethod(LINK_LOGIN);
        System.out.println("Connecting with Token");
        // parameters
        NameValuePair[] data = getData(form);
        if(data.length<1){return false;}

        post.addRequestHeader("referer", LINK_REFER);
        post.addRequestHeader("Content-Type", "application/x-www-form-urlencoded");
        post.addRequestHeader("Accept-Encoding", "gzip,deflate");
        post.setRequestBody(data);
        post.addParameter("Accept-Language",Locale.getDefault().getLanguage());

        int statusCode = -1;
        try
        {
            // execute the method.
            statusCode = client.executeMethod(post);

        }
        catch (IOException e)
        {
            return false;

        }

        // Check we actually get status code correctly
        if (statusCode == -1)
        {
            return false;

        }

        // Get response String & Release the connection.
        String result = null;
        try{
            result = getResponseBodyAsString(post);
        }
        catch(IOException oe)
        {
            return false;
        }
        post.releaseConnection();

        // check if connect failed
        if (result.indexOf("errormsg") > 0)
        {
        	if(result.contains("class=\"errormsg\""))
        		{
        		try
        		{
        			int pre = result.indexOf("class=\"errormsg\"");
        			int errorMsgIni = result.indexOf(">",pre)+1;
        			int errorMsgFim = result.indexOf("</div>",errorMsgIni);
        			String errormsg = result.substring(errorMsgIni,errorMsgFim);
        			errormsg = errormsg.replaceAll("<.*>", "");
        			JOptionPane.showMessageDialog(null, errormsg,"Conta gmail Com problemas", JOptionPane.WARNING_MESSAGE);

        		}
        	catch(Exception ex){}
        		}
        	return false;
            //return false;
        }

        // get _sgh variable from login response
        String var_sgh = "";
        try
        {
            int pointer = result.indexOf("_sgh");
            if(pointer > -1)
            {

                int varDeclStart = result.indexOf("_sgh%3D",pointer) + 7;
                int varDeclEnd = result.indexOf("&",varDeclStart);
                var_sgh = result.substring(varDeclStart, varDeclEnd);
            }

        }
        catch (IndexOutOfBoundsException  iooe)
        {
            return false;
        }

        /* create a get request to perform "cookie-handshaking"...  */
        // https://gmail.google.com/gmail?_sgh=
        GetMethod get = new GetMethod(LINK_GMAIL + "?_sgh=" + var_sgh);
        get.addRequestHeader("referer", LINK_REFER);
        get.addRequestHeader("Content-Type", "text/html");
        get.addRequestHeader("Accept-Encoding", "gzip,deflate");

        statusCode = -1;
        try
        {
            // execute the method.
            statusCode = client.executeMethod(get);
        }
        catch (IOException e)
        {
            return false;
        }

        // redirect?
        if (statusCode == HttpStatus.SC_MOVED_PERMANENTLY  ||
                statusCode == HttpStatus.SC_MOVED_TEMPORARILY  ||
                statusCode == HttpStatus.SC_SEE_OTHER ||
                statusCode == HttpStatus.SC_TEMPORARY_REDIRECT )
        {

            String redirectLocation;
            Header locationHeader = get.getResponseHeader("location");
            if (locationHeader != null)
            {
                redirectLocation = locationHeader.getValue();
                get = new GetMethod(redirectLocation);
                get.addRequestHeader("Accept-Encoding", "gzip,deflate");

                try
                {
                    statusCode = client.executeMethod(get);
                }
                catch (IOException e)
                {
                    return false;
                }
            }
            else
            {
                // The response is invalid and did not provide the new location for
                // the resource.
                return false;
            }
        }

        // 2nd redirect?
        if (statusCode == HttpStatus.SC_MOVED_PERMANENTLY  ||
                statusCode == HttpStatus.SC_MOVED_TEMPORARILY  ||
                statusCode == HttpStatus.SC_SEE_OTHER ||
                statusCode == HttpStatus.SC_TEMPORARY_REDIRECT )
        {

            String redirectLocation;
            Header locationHeader = get.getResponseHeader("location");
            if (locationHeader != null)
            {
                redirectLocation = locationHeader.getValue();
                get = new GetMethod(redirectLocation);
                get.addRequestHeader("Accept-Encoding", "gzip,deflate");

                try
                {
                    statusCode = client.executeMethod(get);
                }
                catch (IOException e)
                {
                    return false;
                }
            }
            else
            {
                // The response is invalid and did not provide the new location for
                // the resource.
                return false;
            }
        }

        String resp = null;
        try
        {
            if (statusCode == HttpStatus.SC_OK)
            {
                resp = getResponseBodyAsString(get);
            }
            get.releaseConnection();
        }
        catch(IOException ioe)
        {
        }

        if (resp == null)
        {
            return false;
        }

        this.status = STATUS_CONNECTED;

        return true;

    }


    private NameValuePair[] getData(String form)
    {

    	Vector<NameValuePair> hdinputs = new Vector<NameValuePair>();

    	form = form.replace("<input","<input#input#");
    	String[] inputs = form.split("<input");

    	String LinkImg = new String();

    	for(String input:inputs)
    	{
    	try
    	{

    	int inputIni = input.indexOf("#input#")+7;
    	int inputEnd = input.indexOf(">",inputIni);
    	input = input.substring(inputIni,inputEnd);

    	if(input.contains("type=\"hidden\"") | input.contains("type='hidden'"))
    	{
    		String nome="";String valor="";

    		//nome
    		int nomeIni = input.indexOf("name=")+5;
    		String tempChar = String.valueOf(input.charAt(nomeIni));
    		if(tempChar.equals("\""))
    		{
    			int nomeFim = input.indexOf("\"",nomeIni+1);
    			nome = input.substring(nomeIni+1,nomeFim);
    		}
    		else{if(tempChar.equals("'"))
    		{
    			int nomeFim = input.indexOf("'",nomeIni+1);
    			nome = input.substring(nomeIni+1,nomeFim);
    		;}}

    		//valor
    		if(input.contains("value=")){
    		int valorIni = input.indexOf("value=")+6;
    		tempChar = String.valueOf(input.charAt(valorIni));
    		if(tempChar.equals("\""))
    		{
    			int valorFim = input.indexOf("\"",valorIni+1);
    			valor = input.substring(valorIni+1,valorFim);
    		}
    		else{if(tempChar.equals("'"))
    		{
    			int valorFim = input.indexOf("'",valorIni+1);
    			valor = input.substring(valorIni+1,valorFim);
    		;}}
    		}

    		if(nome.equals("loginurl")){LinkImg=valor;}
    		hdinputs.addElement(new NameValuePair(nome,valor));
    	}
    	}catch(Exception ex){return new NameValuePair[0];}
    	}

    	try
    	{


    	hdinputs.addElement(new NameValuePair("logincaptcha",pegacodigo(LinkImg)));
    	hdinputs.addElement(new NameValuePair("Email", user));
    	hdinputs.addElement(new NameValuePair("Passwd", passwd));
    	hdinputs.addElement(new NameValuePair("null", "Sign in"));
    	}
    	catch(Exception ex)
    	{return new NameValuePair[0];}

    	int len = hdinputs.size();
    	NameValuePair[] dataReady = new NameValuePair[len];
    	for(int i = 0; i < len;i++)
    	{
    	dataReady[i] = hdinputs.elementAt(i);
    	}

    return 	dataReady;

    }

    private String pegacodigo(String linkfoto)
    {

    	GetMethod get = new GetMethod(linkfoto);
    	get.addRequestHeader("Accept", "image/png");
    	get.addRequestHeader("Connection", "keep-alive");

    	try{
    	client.executeMethod(get);
    	ImageIcon image = new ImageIcon(get.getResponseBody());
    	JLabel jLabel = new JLabel("O que tem na foto?", image,JLabel.CENTER);
    	String codigoImagem=JOptionPane.showInputDialog(jLabel);
    	return codigoImagem;
    	}
    	catch (Exception e)
           {
          System.err.println(e);
                 }
    	return "";
    }



    /**
     * Contact GMail server
     * send the input query to gmail, and parse the returned packets into GMResponse
     * If the returned response include a java script redirect, it would try to follows
     * @param query String
     * @throws IOException
     * @throws ParsePacketException
     * @return GMResponse
     */
    private GMResponse fetch(String query) throws IOException, ParsePacketException
    {
        if (!isConnected())
        {
            throw new IllegalStateException("Not logged in.");
        }
        query += "&ui=1&zv=" + rdm.nextInt(); // to fool proxy

        /* create  post request to fetch data */
        GetMethod get = new GetMethod(LINK_GMAIL + "?" + query);

        get.addRequestHeader("referer", LINK_REFER);
        get.addRequestHeader("Content-Type", "text/html");
        get.addRequestHeader("Accept-Encoding", "gzip,deflate");

        /* retrive the web page */
        try
        {
            // execute the method.
            client.executeMethod(get);
        }
        catch (IOException e)
        {
        }

        // Get response String & Release the connection.
        String input = null;
        input = getResponseBodyAsString(get);
        get.releaseConnection();

        // cleanup incoming data
        input = input.replaceAll("\n", "");
        input = input.replaceAll("D\\(\\[", "\nD([");
        input = input.replaceAll("\\]\\);", "]);\n");

        return parseDataPacket(input);
    }

    /**
     * Send request to gmail
     * @param type byte Must be one of GM_REQ_STANDARD, GM_REQ_LABEL, GM_REQ_QUERY  or GM_REQ_CONVERSATION
     * @param box String label name or directory name or query, e.g. "Inbox"
     * @param pos String Postion of the search as String
     * @return GMResponse response from gmail
     * @throws IOException When reading error from connection
     * @throws ParsePacketException When GMail send something we dont expect
     * @todo figure out in conversation request, what the array parameters do
     */
    public GMResponse request(byte type, String box, String pos) throws IOException, ParsePacketException
    {
        return request(type, box, pos, null);
    }

    /**
     * Send request to gmail
     * @param type byte
     * @param box String
     * @param pos String
     * @param msgs String[] array of msg id, if it is null no such id is sent
     * @throws IOException
     * @throws ParsePacketException
     * @return GMResponse
     * @todo test request with multiple msg id attached
     */
    public GMResponse request(byte type, String box, String pos, String[] msgs) throws IOException, ParsePacketException
    {
        StringBuffer q = new StringBuffer();
        box = URIUtil.encodeQuery(box);
        switch(type)
        {
            case GMConstants.GM_REQ_STANDARD:
                q.append("search=" + box.toLowerCase() + "&view=tl&start=" + pos);
            break;
            case GMConstants.GM_REQ_LABEL:
                q.append("search=cat&cat=" + box + "&view=tl&start=" + pos);
            break;
            case GMConstants.GM_REQ_QUERY:
                q.append("search=query&q=" + box + "&view=tl&start=" + pos);
            break;
            case GMConstants.GM_REQ_CONVERSATION:
                // https://gmail.google.com/gmail?view=cv&search=inbox&th=feec9ee1ae2ca03&zx=509dedf8dd775d9a240425503
                if (msgs != null && msgs.length > 0)
                {
                    q.append("search=inbox&view=cv");
                    for (int i = 0; i < msgs.length; i++)
                        q.append("&msgs=" + msgs[i]);
                }
                else
                {
                    q.append("search=inbox&view=cv&th=" + box);
                }
            break;
        }

        return fetch(q.toString());
    }


    /**
     * Request gmail for the original source of the email
     * Use this method if you do NOT want to get the gmail/gmailer processed
     * message and instead get the untouched form of the original message
     * @param String id ID of the thread
     * @todo retry id HttpRecoverableException occurs
     */
    public String fetchOriginalMail(String id) throws IOException{
        // https://gmail.google.com/gmail?view=om&th=ff6c2309e8337e8&zx=d41deb7018bc6f3a402350082

        String query = "view=om&th=" + id ;

        GetMethod get = new GetMethod(LINK_GMAIL + "?" + query);

        get.addRequestHeader("referer", LINK_REFER);
        get.addRequestHeader("Content-Type", "text/txt");
        get.addRequestHeader("Accept-Encoding", "gzip,deflate");

        /* retrive the web page */

        // execute the method.
        client.executeMethod(get);

        // Get response String & Release the connection.
        String input = getResponseBodyAsString(get);
        get.releaseConnection();

        return input;
    }

    public boolean send(GMSendMailBody mailbody) throws IOException
    {
        if (!isConnected())
        {
            return false;
        }

        /* create post request to send mail */

        PostMethod post = new PostMethod(LINK_GMAIL);

        String gmailat = "";
        Cookie[] cookies = client.getState().getCookies();
        String cookieTxt;
        String[] cookieSplit;
        for(int i=0; i<cookies.length; i++){
            cookieTxt = cookies[i].toString();
            cookieSplit = cookieTxt.split("=");
            if (cookieSplit != null &&
                    cookieSplit[0].equals("GMAIL_AT")){
                gmailat = cookieSplit[1];
                break;
            }
        }

        post.addParameter("view", "sm");
        post.addParameter("msgbody", mailbody.getMsgbody());
        post.addParameter("subject", mailbody.getSubject());
        post.addParameter("to", mailbody.getToAsString());
        post.addParameter("cc", mailbody.getCcAsString());
        post.addParameter("bcc", mailbody.getBccAsString());
        post.addParameter("rm", mailbody.getRm());
        post.addParameter("th", mailbody.getTh());
        post.addParameter("draft", mailbody.getDraft());
        post.addParameter("at", gmailat);
        post.addParameter("cmid", "1");

        String[] attLinks = mailbody.getAttachmentLink();
        for(int i=0; i<attLinks.length; i++){
            post.addParameter("attach", attLinks[i]);
        }

        post.addRequestHeader("referer", LINK_REFER);
        post.addRequestHeader("User-Agent", USER_AGENT);
        post.addRequestHeader("Content-Type", "application/x-www-form-urlencoded");

        int statusCode = -1;
        try
        {
            // execute the method.
            statusCode = client.executeMethod(post);
        }
        catch (IOException e)
        {
            return false;
        }
        finally
        {
            post.releaseConnection();
        }

        // Check we actually get status code correctly
        if (statusCode == -1)
        {
            return false;
        }

        return true;
    }

    /**
     * Open an Attachment in GMal as an InputStream
     * @param attid String Attachment ID
     * @param msgid String Message ID
     * @throws IOException Error reading from gmail
     * @return InputStream
     */
    public InputStream getAttachmentAsStream(String attid, String msgid) throws IOException {
        if (!isConnected()) {
            throw new IllegalStateException("Not logged in.");
        }

        /* create  post request to fetch data */
        String query = "&attid=" + attid + "&th=" + msgid;

        GetMethod get = new GetMethod(GMConnector.LINK_ATTACHMENT + "?" + query);
        get.addRequestHeader("referer", LINK_REFER);
        get.addRequestHeader("Content-Type", "text/html");

        /* retrive the web page */
        try
        {
            // execute the method.
            client.executeMethod(get);
        }
        catch (IOException e)
        {
        }

        // Get response String & Release the connection.
        return get.getResponseBodyAsStream();
    }

    private GMResponse parseDataPacket(String input) throws ParsePacketException
    {
        if (input == null || input.equals(""))
        {
            new ParsePacketException("Empty input!");
        }

        // the prased response
        GMResponse response = null;
        TreeMap gminfo = new TreeMap();
        TreeMap gmlabel = new TreeMap();
        ArrayList threads = new ArrayList();

        // capture all the dataItems
        Pattern pDataItems = Pattern.compile("D\\((\\[.+\\])\\)");
        Matcher mDataItems = pDataItems.matcher(input);

        while(mDataItems.find()){
            // get ArrayList version of dataPack JS array
            ArrayList tmpArray = Utilities.ParseJSArray(mDataItems.group(1));
            String settingName = (String)tmpArray.get(0);
            tmpArray.remove(0);
            try{
                if (settingName.equals("v")) {
                    gminfo.put("version", (String) tmpArray.get(0));
                }
                else if (settingName.equals("ds")) { // default searches
                    if (tmpArray.size() == 7) {
                        gminfo.put("stdbox.inbox", (String) tmpArray.get(0));
                        gminfo.put("stdbox.starred", (String) tmpArray.get(1));
                        gminfo.put("stdbox.sent", (String) tmpArray.get(2));
                        gminfo.put("stdbox.dwafts", (String) tmpArray.get(3));
                        gminfo.put("stdbox.all", (String) tmpArray.get(4));
                        gminfo.put("stdbox.spam", (String) tmpArray.get(5));
                        gminfo.put("stdbox.trash", (String) tmpArray.get(6));
                    }
                    else
                    {
                        /*
                        if(Constantes.DEBUG)
                            logger.warning(
                            "DataPacket error: 'ds' did not have expected number of elements (7).");
                        throw new ParsePacketException(
                                "'ds' did not have expected number of elements (7).",
                                input);
                        */
                    }
                }
                else if (settingName.equals("ct")) { // category(label) list
                    for (Iterator iter = ( (ArrayList) tmpArray.get(0)).iterator();
                    iter.hasNext(); ) {
                        ArrayList label = (ArrayList) iter.next();
                        gmlabel.put( (String) label.get(0), (String) label.get(1));
                    }
                }
                else if (settingName.equals("qu")) { // quota
                    // INFO: Data Item: ["qu","47 MB","1000 MB","5%","#006633"]
                    gminfo.put("quota.mb", (String) tmpArray.get(0));
                    gminfo.put("quota.total", (String) tmpArray.get(1));
                    gminfo.put("quota.percent", (String) tmpArray.get(2));
                }
                else if (settingName.equals("i")) { // invitation
                    gminfo.put("invitation", (String) tmpArray.get(0));
                }
                else if (settingName.equals("ts")) { // thread status
                    /**
                     * if a packet have "ts",
                     * it is a GMStandard type response (query)
                     * Create the object
                     **/
                    response = new GMStandard(gminfo, gmlabel, threads);

                    // 15 Feb 05: D(["ts",0,50,3,0,"Inbox","10213b2b249",4,]
                    // ts",0,20,21,0,"Search results for: Hello","1025e4f50f4",35,,"hello.0"]
                    if (tmpArray.size() >= 8) {
                        gminfo.put("current.box.name", (String) tmpArray.get(4));
                        gminfo.put("current.box.totalthreads", (String) tmpArray.get(2));
                        gminfo.put("current.box.threadsperpage", (String) tmpArray.get(1));
                        gminfo.put("current.box.position", (String) tmpArray.get(0));
                        gminfo.put("current.box.timestamp", (String) tmpArray.get(5));
                        gminfo.put("current.box.totalmsgs", (String) tmpArray.get(6));
                    }
                    else
                    {
                        throw new ParsePacketException(
                                "'ts' did not have expected number of elements (8).",
                                input);
                    }
                }
                else if (settingName.equals("cs")) { // conversation status
                    /**
                     * if a packet have "cs",
                     * it is a GMConversion type response
                     * Create the object
                     **/
                    response = new GMConversation(gminfo, gmlabel, threads);


                    ((GMConversation)response).setTitle((String)tmpArray.get(1));
                    ((GMConversation)response).setTotal(Integer.parseInt((String)tmpArray.get(7)));
                    ((GMConversation)response).setId((String)tmpArray.get(0));
                    ((GMConversation)response).setLabels((ArrayList)tmpArray.get(4));
                }
                else if (settingName.equals("mi")) { // conversation entry
                    /*
                     As of 15 Feb 05
                     "mi",
                     0 0,
                     1 1,
                     2 "1024f1c178a90037",
                     3 0,
                     4 "0",
                     5 "xxxxxx",
                     6 "xxxxxxxxx@gmail.com",
                     7 "me",
                     8 "2/26/05 (3 days ago)",
                     9 ["xxxxxxxxxxx@gmail.com"],
                     10 [],
                     11 [],
                     12 ["xxxx <xxxxxxxxx@gmail.com>"],
                     13 "Sat, 26 Feb 2005 15:39:58 +0100",
                     14 "xxxxxxxxxxxxxxxxx",
                     15 "",
                     16 [],
                     17 0,
                     18 ,
                     19 ,
                     20 "Saturday, February 26, 2005_6:39 AM"]);

                     [
                     "mi",
                     0 8,
                     1 1,
                     2 "104e5fc86adbd621",
                     3 0,
                     4 "0",
                     5 "Google �ְT",
                     6 "Google �ְT",
                     7 "googlealerts-noreply@google.com",
                     8 "me",
                     9 "12:53 am (6 hours ago)",
                     10 ["siu.ying@gmail.com"],
                     11 [],
                     12 [],
                     13 [],
                     14 "Jul 5, 2005 12:53 AM",
                     15 "Google �ְT - ��q�Ȧ�",
                     16 "",
                     17 [],
                     18 0,
                     19 ,
                     20 ,
                     21 "Tue Jul 5 2005_12:53 AM",
                     22 "On 7/5/05, Google �ְT <googlealerts-noreply@google.com> wrote:",
                     23 "On 7/5/05, <b class=gmail_sendername>Google �ְT</b> &lt;googlealerts-noreply@google.com&gt; wrote:"]
                     */
                    GMConversationEntry entry = new GMConversationEntry();
                    entry.setIndex((String)tmpArray.get(1));
                    entry.setId((String)tmpArray.get(2));
                    entry.setSender((String)tmpArray.get(6));
                    entry.setSender_email(((String)tmpArray.get(7)));
                    if (tmpArray.get(8) instanceof ArrayList)
                    {
                        //ENCAMINHADO DO WALLA NEM PRECISA TRATAR...
                        //ArrayList a=(ArrayList)tmpArray.get(8);
                        //System.out.println(a.size());
                        //System.out.println(a.get(0).getClass().getName());
                    }
                    else
                        entry.setRecv((String)tmpArray.get(8));

                    ArrayList atmp = ((ArrayList)tmpArray.get(10));
                    if (atmp.size() > 0) {
                        entry.setRecv_email( (String) atmp.get(0) );
                    }else{
                        entry.setRecv_email( "" );
                    }

                    atmp = ((ArrayList)tmpArray.get(13));
                    if (atmp.size() > 0) {
                        entry.setRecv_email( (String) atmp.get(0) );
                    }else{
                        entry.setRecv_email( "" );
                    }

                    entry.setDt_easy((String)tmpArray.get(9));
                    entry.setDt((String)tmpArray.get(14));

                    entry.setSubj((String)tmpArray.get(15));
                    entry.setSnippet((String)tmpArray.get(16));

                    ArrayList attachments = new ArrayList();

                    // [[0.1, src.zip, application/x-zip-compressed, 6289], 1]
                    for(Iterator iter = ((ArrayList)tmpArray.get(17)).iterator(); iter.hasNext();){
                        Object attachObj = iter.next();
                        if (attachObj.getClass().equals(ArrayList.class)){
                            HashMap atthHash = new HashMap();
                            ArrayList atth = (ArrayList) attachObj;
                            if (atth.size() >4){
                                atthHash.put("id", atth.get(0));
                                atthHash.put("filename", atth.get(1));
                                atthHash.put("type", atth.get(2));
                                atthHash.put("size", atth.get(3));
                                attachments.add(atthHash);
                            }
                        }
                    }
                    entry.setAttachment(attachments);
                    ((GMConversation)response).addEntries(entry);
                }
                else if (settingName.equals("di")){
                    // "di",4,0,"ff6da5ba37d95a7",0,"0","??p??v","siu.ying@gmail.com",
                    // "","2:50am (2 hours ago)","aaaaaaaaaaaaaaa@gmail.com","","","??p??v \<siu.ying@gmail.com\>","Wed, 6 Oct 2004 17:50:38 +0800","","",[],1,"","aaaaaaaaaaaaaaaaaa\n"]
                    GMConversationEntry entry = new GMConversationEntry();
                    entry.setIndex( (String) tmpArray.get(1));
                    entry.setId( (String) tmpArray.get(2));
                    entry.setSender( (String) tmpArray.get(5));
                    entry.setSender_email( ( (String) tmpArray.get(6)));

                    entry.setRecv( (String) tmpArray.get(7));
                    entry.setRecv_email( ( (String) tmpArray.get(9)));
                    entry.setReply_email( ( (String) tmpArray.get(12)));

                    entry.setDt_easy( (String) tmpArray.get(8));
                    entry.setDt( (String) tmpArray.get(13));

                    entry.setSubj( (String) tmpArray.get(14));
                    entry.setSnippet( (String) tmpArray.get(15));
                    entry.appendBody((String) tmpArray.get(19));

                    ArrayList attachments = new ArrayList();

                    // [[0.1, src.zip, application/x-zip-compressed, 6289], 1]
                    for (Iterator iter = ( (ArrayList) tmpArray.get(16)).iterator();
                    iter.hasNext(); ) {
                        Object attachObj = iter.next();
                        if (attachObj.getClass().equals(ArrayList.class)) {
                            HashMap atthHash = new HashMap();
                            ArrayList atth = (ArrayList) attachObj;
                            atthHash.put("id", atth.get(0));
                            atthHash.put("filename", atth.get(1));
                            atthHash.put("type", atth.get(2));
                            atthHash.put("size", atth.get(3));
                            attachments.add(atthHash);
                        }
                    }
                    entry.setAttachment(attachments);
                    ( (GMConversation) response).addEntries(entry);

                }else if (settingName.equals("mb")) { // message body
                    try{
                        String id = (String)tmpArray.get(1);
                        // append the message body we found to last entry
                        if (id != null){
                            String body = (String)tmpArray.get(0);

                            ArrayList entries = ((GMConversation)response).getEntries();
                            try
                            {
                                GMConversationEntry entry = (GMConversationEntry)entries.get(entries.size()-1);
                                entry.appendBody(body);
                            }
                            catch(ClassCastException cce)
                            {
                            }
                        }
                    }
                    catch(NullPointerException npe)
                    {
                    }
                    catch(ClassCastException cce)
                    {
                    }
                    catch(NumberFormatException nfe)
                    {
                    }
                }
                else if (settingName.equals("t")) { // threads (message lists)
                    // create array to store message listing
                    for (Iterator iter = tmpArray.iterator(); iter.hasNext(); ) {
                        ArrayList msgArray = (ArrayList) iter.next();
                        GMThread msg = new GMThread();
                        msg.setThreadID( (String) msgArray.get(0));
                        msg.setRead( ((String) msgArray.get(1)).equals("1") ? false : true); // Gmail reports isUnread so we swap
                        msg.setStarred( ((String) msgArray.get(2)).equals("1") ? true : false);
                        msg.setDateHtml( (String) msgArray.get(3));
                        msg.setAuthorsHtml( (String) msgArray.get(4));
                        msg.setFlags( (String) msgArray.get(5));
                        msg.setSubjectHtml( (String) msgArray.get(6));
                        msg.setSnippetHtml( (String) msgArray.get(7));
                        msg.setCategories( (ArrayList) msgArray.get(8));
                        msg.setAttachHtml( (String) msgArray.get(9));
                        msg.setMatchingMessageID( (String) msgArray.get(10));
                        threads.add(msg);
                    }

                }
            }catch(ClassCastException ce)
            {
                ce.printStackTrace();
                throw new ParsePacketException("failed parsing label packet (" + settingName + ") ClassCastException " + ce.getMessage(),
                        input);
            }
            catch(NullPointerException ne)
            {
                ne.printStackTrace();
                throw new ParsePacketException("failed parsing label packet (" + settingName + ") NullPointerException " + ne.getMessage(),
                        input);
            }
            catch(ArrayIndexOutOfBoundsException ae)
            {
                throw new ParsePacketException("failed parsing label packet (" + settingName + ") ArrayIndexOutOfBoundsException " + ae.getMessage(),
                        input);
            }
        }

        if (response == null)
        {
            throw new ParsePacketException("Unknown message type!", input);
        }

        return response;
    }
}
